package com.hongji.lms5xxcommunicator.service.imp;

import com.hongji.lms5xxcommunicator.entity.GridBootstrapParam;
import com.hongji.lms5xxcommunicator.entity.GridSnapshotDto;
import com.hongji.lms5xxcommunicator.entity.ScanPointEntity;
import com.hongji.lms5xxcommunicator.entity.SnapshotCache;
import com.hongji.lms5xxcommunicator.grid.RasterGrid;
import com.hongji.lms5xxcommunicator.grid.config.RasterGridHolder;
import com.hongji.lms5xxcommunicator.grid.core.GridSpec;
import com.hongji.lms5xxcommunicator.grid.export.GridPoint;
import com.hongji.lms5xxcommunicator.mapper.ScanPointDataMapper;
import com.hongji.lms5xxcommunicator.service.GridBootstrapService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.cursor.Cursor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicLong;

@Slf4j
@Service
@RequiredArgsConstructor
public class GridBootstrapServiceImp implements GridBootstrapService {

    private final ScanPointDataMapper mapper;
    private final RasterGridHolder gridHolder;
    private final SnapshotCache snapshotCache;

    /** 与实时保持一致：不再做二次翻转 */
    @Value("${grid.export.row-origin:bottom}")
    private String rowOrigin; // bottom | top
    private boolean isBottom() { return !"top".equalsIgnoreCase(rowOrigin); }

    // ===== 配色相关（不建配置类，直接 yml 注入，带默认值）=====
    @Value("${grid.color.freeze:true}")
    private boolean colorFreeze;                 // 回灌后是否冻结色尺
    @Value("${grid.color.percentile.lo:0.02}")
    private double pctLo;                        // 低位百分位
    @Value("${grid.color.percentile.hi:0.98}")
    private double pctHi;                        // 高位百分位
    @Value("${grid.color.sample:20000}")
    private int sampleCap;                       // 水塘抽样容量
    @Value("${grid.color.eps:0.0}")
    private double zEps;                         // 去抖阈值（mm）

    @Override
    @Transactional(readOnly = true)
    public GridSnapshotDto buildSnapshot(GridBootstrapParam p) {
        // 1) 规格解析：优先入参六要素，否则用系统规格
        GridSpec spec = resolveSpec(p);

        // 2) 分页参数
        final int pageSize = Math.max(1, Optional.ofNullable(p.getPageSize()).orElse(50_000));
        final int offset   = parseCursor(p.getCursor()); // 0 / 50000 / 100000 ...

        // 3) 缓存：把 page 维度纳入 key（pageSize + offset），避免各页都命中第一页
        String baseKey  = SnapshotCache.keyOf(spec, p);
        String cacheKey = baseKey
                + "|rowOrigin=" + (isBottom() ? "bottom" : "top")
                + "|pageSize=" + pageSize
                + "|offset="   + offset;

        GridSnapshotDto cached = snapshotCache.get(cacheKey);
        if (cached != null) {
            if (log.isDebugEnabled()) log.debug("[Bootstrap] cache hit key={}", cacheKey);
            return cached;
        }

        // 4) 用 *当前* 规格构建临时网格（与实时一致：默认 REPLACE 策略）
        RasterGrid grid = new RasterGrid(spec);

        // —— 首帧色尺的稳健估计：水塘抽样 + 百分位 —— //
        final int RES = Math.max(1000, sampleCap);
        final java.util.Random rnd = new java.util.Random(202503);
        final java.util.ArrayList<Double> zReservoir = new java.util.ArrayList<>(RES);
        AtomicLong seenZ = new AtomicLong(0L);
        java.util.function.Consumer<Double> offerZ = (Double z) -> {
            if (z == null) return;
            seenZ.getAndIncrement();
            if (zReservoir.size() < RES) { zReservoir.add(z); return; }
            int j = rnd.nextInt((int)Math.min(Integer.MAX_VALUE, seenZ.get()));
            if (j < RES) zReservoir.set(j, z);
        };

        // 5) 流式读库 → 批量合并到网格
        final double xMin = Math.min(spec.x0, spec.x1);
        final double xMax = Math.max(spec.x0, spec.x1);
        final double yMin = Math.min(spec.y0, spec.y1);
        final double yMax = Math.max(spec.y0, spec.y1);

        long count = 0L;
        long t0 = System.currentTimeMillis();
        final int BATCH = 10_000;
        List<ScanPointEntity> bucket = new ArrayList<>(BATCH);

        try (Cursor<ScanPointEntity> cursor =
                     mapper.scanPointsCursor(xMin, xMax, yMin, yMax)) {
            for (ScanPointEntity e : cursor) {
                if (e == null) continue;
                // ⭐ 采样 z，用于稳健色尺
                offerZ.accept(e.getZ());

                bucket.add(e);
                if (bucket.size() >= BATCH) {
                    // 内部做越界过滤 / 对齐格中心 / 更新 zRange / 体积失效
                    grid.mergePoints(bucket);
                    bucket.clear();
                }
                count++;
            }
            if (!bucket.isEmpty()) {
                grid.mergePoints(bucket);
                bucket.clear();
            }
        } catch (Exception ex) {
            throw new RuntimeException("scanPointsCursor 失败", ex);
        }

        long t1 = System.currentTimeMillis();
        log.info("[Bootstrap] DB→Grid 完成: points={}, elapsed={}ms, bbox=[{},{}]x[{},{}]",
                count, (t1 - t0), xMin, xMax, yMin, yMax);

        // 5.5) 计算稳健色尺（百分位），并（可选）冻结
        double lo = grid.getObservedMinZ();
        double hi = grid.getObservedMaxZ();
        if (!zReservoir.isEmpty()) {
            java.util.Collections.sort(zReservoir);
            int nS = zReservoir.size();
            int iLo = (int)Math.floor(Math.max(0, Math.min(nS - 1, nS * pctLo)));
            int iHi = (int)Math.floor(Math.max(0, Math.min(nS - 1, nS * pctHi)));
            lo = zReservoir.get(Math.min(iLo, iHi));
            hi = zReservoir.get(Math.max(iLo, iHi));
            if (!(hi > lo)) hi = lo + 1.0; // 退化保护：平面时给个极小带宽
        }

        // 去抖阈值
        grid.setZRangeEps(zEps);

        // 冻结到临时网格（这样导出 zMin/zMax 就是稳健范围）
        if (colorFreeze) {
            grid.freezeColorRange(lo, hi);
            log.info("[Bootstrap] 冻结色尺: lo={} hi={} (pctLo={} pctHi={} reservoir={})",
                    lo, hi, pctLo, pctHi, zReservoir.size());
        }

        // 同步冻结到“实时网格”（全局那只），确保 WS 后续也保持同一配色
        try {
            RasterGrid live = gridHolder.get(); // 你的 Holder 已提供 get()
            if (live != null) {
                live.setZRangeEps(zEps);
                if (colorFreeze) {
                    live.freezeColorRange(lo, hi);
                    log.info("[Bootstrap] 已同步冻结到实时网格");
                }
            }
        } catch (Throwable ex) {
            log.warn("[Bootstrap] 同步冻结到实时网格失败：{}", ex.toString());
        }

        // 6) 体积（单位 m³，RasterGrid 内部已做单位换算）
        double volumeM3 = grid.getVolume();

        // 7) 导出稀疏点 -> 分页切片（不翻转，保持与实时一致）
        List<GridPoint> ptsAll = grid.exportGridPoints(true, 1, null);
        final int total = (ptsAll == null) ? 0 : ptsAll.size();
        final int from  = Math.min(Math.max(0, offset), total);
        final int to    = Math.min(from + pageSize, total);
        List<GridPoint> page = (from < to) ? ptsAll.subList(from, to) : Collections.emptyList();

        // 8) 组装 DTO（只装本页 cells）
        GridSnapshotDto.Spec specDto = new GridSnapshotDto.Spec();
        specDto.setX0(spec.x0);
        specDto.setY0(spec.y0);
        specDto.setX1(spec.x1);
        specDto.setY1(spec.y1);
        specDto.setRows(spec.rows);
        specDto.setCols(spec.cols);
        specDto.setDx(spec.dx);
        specDto.setDy(spec.dy);
        specDto.setUnit("mm");

        int n = page.size();
        int[] r = new int[n];
        int[] c = new int[n];
        double[] z = new double[n];

        final int rows = spec.rows;
        for (int i = 0; i < n; i++) {
            GridPoint gp = page.get(i);
            int rr = gp.getRow();
            int cc = gp.getCol();
            r[i] = isBottom() ? (rows - 1 - rr) : rr;
            c[i] = cc;
            z[i] = gp.getZ();
        }
        GridSnapshotDto.Cells cells = new GridSnapshotDto.Cells();
        cells.setR(r);
        cells.setC(c);
        cells.setZ(z);

        GridSnapshotDto.Volume vol = new GridSnapshotDto.Volume();
        vol.setValue(volumeM3);
        vol.setUnit("m3");

        boolean hasMore = to < total;
        String nextCursor = hasMore ? String.valueOf(to) : null;

        GridSnapshotDto dto = new GridSnapshotDto();
        dto.setSpec(specDto);
        dto.setCells(cells);
        dto.setVolume(vol);
        dto.setCount(n);
        dto.setTotal((long) total);
        dto.setHasMore(hasMore);
        dto.setNextCursor(nextCursor);

        // 9) 回填前端颜色范围与序号（这里就是稳健/冻结后的范围）
        dto.setZMin(grid.getObservedMinZ());
        dto.setZMax(grid.getObservedMaxZ());
        dto.setRangeSeq(grid.getRangeSeq());

        // 10) 缓存并返回（包含页维度）
        snapshotCache.put(cacheKey, dto);
        snapshotCache.update(dto);
        return dto;
    }

    /** 从入参或系统默认构造 GridSpec（若传 rows/cols，则由 x0,y0 + dx,dy 推导 x1,y1） */
    private GridSpec resolveSpec(GridBootstrapParam p) {
        if (p != null && p.hasFullSpec()) {
            double x0 = p.getX0();
            double y0 = p.getY0();
            double dx = p.getDx();
            double dy = p.getDy();
            double x1 = x0 + p.getCols() * dx;
            double y1 = y0 + p.getRows() * dy;
            return new GridSpec(x0, x1, y0, y1, dx, dy);
        }
        return gridHolder.getSpec();
    }

    /** "12345" → 12345；空/非法 → 0 */
    private int parseCursor(String cursor) {
        if (cursor == null || cursor.trim().isEmpty()) return 0;
        try {
            return Math.max(0, Integer.parseInt(cursor.trim()));
        } catch (NumberFormatException ignore) {
            return 0;
        }
    }
}
